package drp.snowbell.entity;

import drp.snowbell.util.StrHelper;

/**
 * 模块之间的关系类. 获取数据的时候如果是"一对一"则可以根据 JOIN关联 获取到, 如果是"多对一"的话则使用延迟技术获取.
 * <b>注意:如果是一对多,或者多对多的关系,属性类型必须是Collection</b>.
 * 
 * @author woj
 * 
 */
public class Relation {
	/**
	 * 关系名称.
	 */
	private final String name;
	/**
	 * 主模块名称.
	 */
	private final String mBeanName;
	/**
	 * 关联模块名称.
	 */
	private final String rBeanName;
	/**
	 * 主模块关联的字段名.如果不是使用关联表关联的话,可不填.
	 */
	private final String mFieldName;
	/**
	 * 关联模块的字段名.
	 */
	private final String rFieldName;
	/**
	 * 主要模块的属性.必填,因为要获取其值来生产SQl语句.
	 */
	private final Property mProperty;
	/**
	 * 关联模块的字段名.可以为空,但是如果为空的话rFieldName必填
	 */
	private final Property rProperty;
	/**
	 * 关系表名称.
	 */
	private final String relTable;

	private final String relTableMFieldName;

	private final String relTableRFieldName;

	/**
	 * 关联类型.
	 */
	private final RelationType relationType;

	private final boolean isPureRelTable;

	public Relation(RelationType relationType, String beanName, String relBeanName, String fieldName, String relFieldName, Property mainProperty,
			Property relProperty, String relTable, String relTableFieldName, String relTableRelFieldName, boolean isAutoFetch,
			boolean isAutoUpdateRelation, boolean isPureRelTable) {

		// 下面进行检查值是否正确
		if (mainProperty == null) {
			throw new IllegalArgumentException("mainProperty");
		}
		if (StrHelper.isNullOrEmpty(fieldName)) {
			fieldName = mainProperty.getDbFieldName();
		}
		if (StrHelper.isNullOrEmpty(relFieldName)) {
			if (relProperty == null) {
				throw new IllegalArgumentException("relFieldName");
			} else {
				relFieldName = relProperty.getDbFieldName();
			}
		}
		if (relationType == RelationType.ManyToMany && StrHelper.isNullOrEmpty(relTable)) {
			throw new IllegalArgumentException("relTable");
		}

		this.name = generateRelName(beanName, mainProperty.getName(), relBeanName, relProperty.getName());
		this.mBeanName = beanName;
		this.mFieldName = fieldName;
		this.rBeanName = relBeanName;
		this.rFieldName = relFieldName;
		this.rProperty = relProperty;
		this.mProperty = mainProperty;
		this.relationType = relationType;
		this.relTable = relTable;
		this.relTableMFieldName = relTableFieldName;
		this.relTableRFieldName = relTableRelFieldName;
		this.isAutoFetch = isAutoFetch;

		this.isPureRelTable = isPureRelTable;
		this.isAutoUpdateRelation = isAutoUpdateRelation;
	}

	/**
	 * 获取主要模块的DNA
	 * 
	 * @return DNA
	 */
	public Dna getMDna() {
		return Farm.getDna(mBeanName);
	}

	/**
	 * 获取关联模块的DNA
	 * 
	 * @return DNA
	 */
	public Dna getRDna() {
		return Farm.getDna(rBeanName);
	}

	/**
	 * 主模块名称.
	 * 
	 * @return 主模块名称
	 */
	public String getMBeanName() {
		return mBeanName;
	}

	/**
	 * 关联模块名称
	 * 
	 * @return
	 */
	public String getRBeanName() {
		return rBeanName;
	}

	/**
	 * 
	 * 主模块关联的字段名,注意此字段名<b>不一定</b>在模块里定义了属性.
	 * 
	 * @return 字段名
	 */
	public String getMFieldName() {
		return mFieldName;
	}

	/**
	 * 
	 * 关联模块关联的字段名,注意此字段名<b>必须</b>在模块里定义了属性.
	 * 
	 * @return 字段名
	 */
	public String getRFieldName() {
		return rFieldName;
	}

	/**
	 * 关系表名称.如果不为空说明存在关联表
	 * 
	 * @return 关系表名称
	 */
	public String getRelTable() {
		return relTable;
	}

	/**
	 * 关联类型.
	 * 
	 * @return 关联类型
	 */
	public RelationType getRelationType() {
		return relationType;
	}

	/**
	 * 关系名称.
	 * 
	 * @return 关系名称
	 */
	public String getName() {
		return name;
	}

	/**
	 * 获取副属性
	 * 
	 * @return
	 */
	public Property getRProperty() {
		return rProperty;
	}

	/**
	 * 是否自动获取关联数据. 使用自动的时候将产生一个代理类会影响运行效率,建议设置成false然后由程序自己处理.
	 * 当关系类型为"一对一"时候用Jion查询获取数据. 当关系类型为"多对一"时候则用晚绑定技术获取.
	 * 如果为true的话一旦该属性调用getter然后自动从数据库里获取数据. 如果为false,则由代码自己来处理.
	 * 
	 * @return ture 为开启自动加载,false为关闭
	 */
	// public boolean isAutoFetch() {
	// return isAutoFetch;
	// }

	/**
	 * 是否自动更新关联,isAutoFetch用于加载数据,此属性用于保存实体之间的关联(不是关联实体).
	 * 
	 * @return
	 */
	// public boolean isAutoUpdateRelation() {
	// return isAutoUpdateRelation;
	// }

	/**
	 * 关联的主属性.注意:此函数跟Relation.getProperty的区别.
	 * 
	 * @return
	 */
	public Property getMProperty() {
		return mProperty;
	}

	/**
	 * 如果存在关联表则此为关联表里的对应filedName的字段名
	 * 
	 * @return 关联表的主要字段名
	 */
	public String getRelTableMFieldName() {
		return relTableMFieldName;
	}

	/**
	 * 如果存在关联表则此为关联表里的对应relFiledName的字段名
	 * 
	 * @return 关联表的关联字段名
	 */
	public String getRelTableRFieldName() {
		return relTableRFieldName;
	}

	/**
	 * 是否跟多条数据发生关系.如果是多条的话该属性类型必须是Collection.
	 * 
	 * @return 是返回true
	 */
	public boolean hasMany() {
		RelationType type = getRelationType();
		return type == RelationType.OneToMany | type == RelationType.ManyToMany;
	}

	/**
	 * 生成关系名称
	 * 
	 * @param mEntityName
	 *            主实体名称
	 * @param mPropName
	 *            主属性名称
	 * @param rEntityName
	 *            关联实体名称
	 * @param rPropName
	 *            关联属性名称
	 * @return 关系名称
	 */
	public static String generateRelName(String mEntityName, String mPropName, String rEntityName, String rPropName) {
		return mEntityName + "_" + mPropName + "$" + rEntityName + "_" + rPropName;
	}

	public boolean isPureRelTable() {
		return isPureRelTable;
	}
}
